package jlib.component.scroll
{
	import jlib.component.core.Background;
	import jlib.component.core.Border;
	import jlib.component.core.BorderPosition;
	import jlib.component.core.BorderType;
	import jlib.component.scroll.skin.ScrollBarSkinFactory;
	
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.MouseEvent;
	import flash.geom.Rectangle;
	
	/**
	 * @example <listing version="3.0" >
	 * var pane:ScrollPane = new ScrollPane;
	 * pane.setView(new Rectangle(0,0,100,80)); // 보여지는 영역을 설정합니다.
	 * pane.setContent(target); // pane의 target 설정합니다. 
	 * </listing>
	 * ScrollPane Class 
	 * @author Park Jong Hee
	 */	
	public class ScrollPane extends Sprite
	{
		static private const DEFAULT_WIDTH:Number = 100;
		static private const DEFAULT_HEIGHT:Number = 100; 
		
		
		private var m_scaleX:Number = 1;
		private var m_scaleY:Number = 1;

		private var m_content:DisplayObjectContainer;
		
		private var m_vscrollBar:ScrollBar;
		private var m_hscrollBar:ScrollBar;

		private var m_hScrollBarVisibleState:Boolean;
		private var m_vScrollBarVisibleState:Boolean;

		private var m_autoScrollBarVisibleState:Boolean;

		private var m_view:Rectangle;
		private var m_border:Border;
		private var m_background:Background;
		
		private var m_viewContainer:Sprite;
		
		// TODO: 디스플레이 오브젝트들의 좌표값 바뀔 때 동기화
		/**
		 * ScrollPane 생성자. 
		 */		
		public function ScrollPane($hscrollBar:ScrollBar=null, $vscrollBar:ScrollBar=null)
		{
			m_border = new Border(1, 0xcccccc, 0, BorderType.SOILD, BorderPosition.INNER);
			m_background = new Background();
			m_viewContainer = new Sprite();
			
			setHorizontalScrollBar($hscrollBar);
			setVerticalScrollBar($vscrollBar);
			init();
		}

		private function init(): void
		{
			setView(new Rectangle(0,0,100,100));
			addChildAt(m_background, 0);
			addChildAt(m_viewContainer, 1);
			if(!m_hscrollBar) initHScrollBar();
			if(!m_vscrollBar) initVScrollBar();
			addChild(m_border);
			addEventListener(Event.ADDED_TO_STAGE, update );
		}
		
		private function initHScrollBar(): void 
		{
			m_hscrollBar = new HScrollBar;
			m_hscrollBar.setTrack(ScrollBarSkinFactory.getHorizontalTrack());
			m_hscrollBar.setThumb(ScrollBarSkinFactory.getHorizontalThumb());
			m_hscrollBar.setDirection(ScrollBarDirection.HORIZONTAL);
			setHScrollBarVisible(true);
			addChild(m_hscrollBar);
		}
		
		private function initVScrollBar(): void
		{
			m_vscrollBar = new VScrollBar;
			m_vscrollBar.setTrack(ScrollBarSkinFactory.getVerticalTrack());
			m_vscrollBar.setThumb(ScrollBarSkinFactory.getVerticalThumb());
			m_vscrollBar.setDirection(ScrollBarDirection.VERTICAL);
			setVScrollBarVisible(true);
			addChild(m_vscrollBar);
		}
		
		/**
		 * 가로 ScrollBar 리턴합니다. 
		 * @return ScrollBar 객체.
		 */		
		public function getHorizontalScrollBar(): ScrollBar { return m_hscrollBar; }
		
		public function setHorizontalScrollBar($scrollBar:ScrollBar): void 
		{
			if(m_hscrollBar && contains(m_hscrollBar)) removeChild(m_hscrollBar);
			
			m_hscrollBar = $scrollBar;
			if(m_hscrollBar){
//				m_hscrollBar.addEventListener(Event.CHANGE, dispatchEvent );
				if(!contains(m_hscrollBar)) addChild(m_hscrollBar);
			}
		}
		
		/**
		 * 세로 ScrollBar 리턴합니다.
		 * @return ScrollBAr 객체.
		 */		
		public function getVerticalScrollBar(): ScrollBar { return m_vscrollBar; }
		
		public function setVerticalScrollBar($scrollBar:ScrollBar): void
		{
			if(m_vscrollBar && contains(m_vscrollBar)) removeChild(m_vscrollBar);
			
			m_vscrollBar = $scrollBar;
			if(m_vscrollBar){
//				m_vscrollBar.addEventListener(Event.CHANGE, dispatchEvent );
				if(!contains(m_vscrollBar)) addChild(m_vscrollBar);				
			}
		}
		
		/**
		 * ScrollPane 의 보이는 영역을 반환합니다. 
		 * @return Rectangle 객체;
		 */		
		public function getView(): Rectangle { return m_view; }
		
		/**
		 * ScrollPane의 보이는 영역을 설정합니다. 
		 * @param $view Rectangle 객체.
		 */		
		public function setView($view:Rectangle): void
		{
			m_view = $view;
			m_background.width = m_border.width = m_view.width;
			m_background.height = m_border.height = m_view.height;
//			update();
		}

		/**
		 * ScrollPane의 Border를 설정합니다. 
		 * @param $border 
		 * @param $color
		 */		
		public function setBorder($thickness:int=0, $color:uint=0xcccccc, $type:String="solid"): void
		{
			m_border.setThickness($thickness);
			m_border.setColor($color);
			m_border.setType($type);
//			update();
		}
		
		/**
		 * 자동으로 스크롤바의 보이기를 설정합니다. 
		 * @param $boolean boolean 값
		 */		
		public function setAutoScrollBarVisible($boolean:Boolean): void
		{
			this.m_autoScrollBarVisibleState = $boolean;
//			update();
		}

		/**
		 * 가로 스크롤바 보이기 유무를 설정합니다.
		 * @param $boolean boolean 값 
		 */		
		public function setHScrollBarVisible($boolean:Boolean): void
		{
			m_hscrollBar.visible = m_hScrollBarVisibleState = $boolean;
			m_hscrollBar.setInteractivable(m_hScrollBarVisibleState);
			m_hscrollBar.setEnabled(m_hScrollBarVisibleState);
			update();
		}
		
		/**
		 * 세로 스크롤바 보이기 유무를 설정합니다.
		 * @param $boolean
		 */		
		public function setVScrollBarVisible($boolean:Boolean): void
		{
			m_vscrollBar.visible = m_vScrollBarVisibleState = $boolean;
			m_vscrollBar.setInteractivable(m_vScrollBarVisibleState);
			m_vscrollBar.setEnabled(m_vScrollBarVisibleState);
			update();
		}

		/**
		 * ScrollPane에 들어갈 Content 객체.
		 * @param $content DisplayObject 객체.
		 */		
		public function setContent($content:DisplayObject): void
		{
			if(this.m_content){
				m_viewContainer.removeChild(m_content);
				m_content = null;
			}
			if($content is DisplayObjectContainer){
				m_content = $content as DisplayObjectContainer;
			}else {
				m_content = new Sprite();
				m_content.addChild($content);
			}
			m_hscrollBar.setTarget(m_content);
			m_vscrollBar.setTarget(m_content);
			
			m_viewContainer.addChild(m_content);
			update();
		}

		/**
		 * 가로 스크롤바의 위치를 설정합니다.
		 * @see com.aitmedia.component.scroll.ScrollBar
		 * @param rate
		 */		
		public function setHPosition(rate:Number): void { m_hscrollBar.setPosition(rate); }

		/**
		 * 세로 스크롤바의 위치를 설정합니다.
		 * @see com.aitmedia.component.scroll.ScrollBar
		 * @param rate
		 */		
		public function setVPosition(rate:Number): void { m_vscrollBar.setPosition(rate); }
		
		/**
		 * ScollPane의 Background를 설정한다.
		 * @param $source DisplayObject, uint 
		 */
		public function setBackground($source:*): void
		{
			m_background.setSource($source);
//			update();
		}
		
		public function update(e:Event=null): void
		{
			if(!stage) {
				this.addEventListener(Event.ADDED_TO_STAGE, update ) ;
				return ;
			}else {
				if(hasEventListener(Event.ADDED_TO_STAGE)){
					stage.addEventListener(Event.RENDER, update );
					stage.addEventListener(MouseEvent.MOUSE_WHEEL, onWheel );
					stage.invalidate();
					this.removeEventListener(Event.ADDED_TO_STAGE, update ) ;
				}else{
					stage.removeEventListener(Event.RENDER, update );
				}
			}
			
			arrangeScrollBar();
			procScrollRect();

			if(m_hscrollBar.getEnabled()){
				if(m_autoScrollBarVisibleState) m_hscrollBar.visible = true;
				m_hscrollBar.setPosition(m_hscrollBar.getPosition());
			} else {
				if(m_autoScrollBarVisibleState) m_hscrollBar.visible = false;
			}
			if(m_vscrollBar.getEnabled()) {
				if(m_autoScrollBarVisibleState) m_vscrollBar.visible = true;
				m_vscrollBar.setPosition(m_vscrollBar.getPosition());
			}else {
				if(m_autoScrollBarVisibleState) m_vscrollBar.visible = false;
			}
			
			m_hscrollBar.update();
			m_vscrollBar.update();
		}
		
		public function addWheelListener():void
		{
			m_hscrollBar.setEnabled(true);
			m_vscrollBar.setEnabled(true);
			stage.addEventListener(MouseEvent.MOUSE_WHEEL, onWheel );
		}
		public function removeWheelListener():void
		{
			m_hscrollBar.setEnabled(false);
			m_vscrollBar.setEnabled(false);
			stage.removeEventListener(MouseEvent.MOUSE_WHEEL, onWheel );
		}
			
		private function onWheel(event:MouseEvent):void
		{
			var wheelArea:Rectangle = this.getBounds(stage);
			
			if(!wheelArea.contains(event.stageX, event.stageY)) return ;
			if(m_vScrollBarVisibleState && m_hScrollBarVisibleState){
				if(m_vscrollBar.getEnabled()){
					m_hscrollBar.setWheelSpeed(0);
//					if(m_vscrollBar.isMaxPosition()){
//						/*if(!m_hscrollBar.isMaxPosition())*/ m_hscrollBar.setWheelSpeed(m_vscrollBar.getWheelSpeed());
//					}else if(m_vscrollBar.isMinPosition()){
//						/*if(!m_hscrollBar.isMinPosition())*/ m_hscrollBar.setWheelSpeed(m_vscrollBar.getWheelSpeed());
//					}
					if(m_vscrollBar.isMaxPosition() || m_vscrollBar.isMinPosition()) m_hscrollBar.setWheelSpeed(m_vscrollBar.getWheelSpeed());
				}else {
					m_hscrollBar.setWheelSpeed(m_vscrollBar.getWheelSpeed());
				}
			}
		}
		
		private function arrangeScrollBar(): void
		{
			if(!m_content) return ;
			var bound:Rectangle = m_border.getBounds(m_border);
			
			m_hscrollBar.y = bound.height - m_hscrollBar.height;
			m_vscrollBar.x = bound.width - m_vscrollBar.width;
			
			var hRange:Number = m_view.width - m_vscrollBar.getGuide();
			var vRange:Number = m_view.height - m_hscrollBar.getGuide();
			
			if(!m_hscrollBar.visible) hRange = m_view.width - m_vscrollBar.width - m_vscrollBar.getGuide();
			if(!m_vscrollBar.visible) vRange = m_view.height - m_hscrollBar.height - m_hscrollBar.getGuide();
			if(m_hscrollBar.visible && m_vscrollBar.visible) {
				hRange = m_view.width - m_vscrollBar.width - m_vscrollBar.getGuide();
				vRange = m_view.height - m_hscrollBar.height - m_hscrollBar.getGuide();
			}
				
			m_hscrollBar.setRange(hRange);
			m_vscrollBar.setRange(vRange);
			
			if(m_hScrollBarVisibleState) m_vscrollBar.setTrackSize(m_view.height-m_hscrollBar.height-m_border.getThickness());
			else m_vscrollBar.setTrackSize(m_view.height-m_border.getThickness());
			
			if(m_vScrollBarVisibleState) m_hscrollBar.setTrackSize(m_view.width-m_vscrollBar.width-m_border.getThickness());
			else m_hscrollBar.setTrackSize(m_view.width-m_border.getThickness());
		}

		private function procScrollRect(): void { 
			this.scrollRect = m_view;
			var rect:Rectangle = new Rectangle(m_view.x, m_view.y, m_view.width-m_vscrollBar.width, m_view.height-m_hscrollBar.height);
			
			if(!m_hscrollBar.visible) rect.height = m_view.height;
			if(!m_vscrollBar.visible) rect.width = m_view.width;
			
			m_viewContainer.scrollRect = rect;
			
			m_hscrollBar.setWheelArea(this.getBounds(stage));
			m_vscrollBar.setWheelArea(this.getBounds(stage));
		}

		override public function get width(): Number { return m_background.width;  }
		override public function set width(value:Number): void { 
			m_view.width = value;
			m_background.width = m_border.width = m_view.width;
		}

		override public function get height(): Number { return m_background.height; }
		override public function set height(value:Number): void { 
			m_view.height = value;
			m_background.height = m_border.height = m_view.height;
		}

		override public function get scaleX(): Number { return m_scaleX; }
		override public function set scaleX(value:Number): void { 
			m_scaleX = value;
		}

		override public function get scaleY(): Number { return m_scaleY; }
		override public function set scaleY(value:Number): void { 
			m_scaleY = value;
		}
		
		override public function addChild(child:DisplayObject):DisplayObject
		{
			var result:DisplayObject = child;
			super.addChildAt(m_background, 0);
			super.addChildAt(m_viewContainer, 1);
			super.addChild(result);
			super.addChild(m_border);
			return result;	
		}
		
		override public function addChildAt(child:DisplayObject, index:int):DisplayObject
		{
			var result:DisplayObject = child;
			super.addChildAt(m_background, 0);
			super.addChildAt(m_viewContainer, 1);
			super.addChildAt(result, index);
			super.addChild(m_border);
			return result;
		}
		
		override public function getBounds(targetCoordinateSpace:DisplayObject):Rectangle
		{
			return m_background.getBounds(targetCoordinateSpace);
		}
		
		override public function getRect(targetCoordinateSpace:DisplayObject):Rectangle
		{
			return m_background.getRect(targetCoordinateSpace);
		}
		
		public function addChildBack(child:DisplayObject):DisplayObject
		{
			var result:DisplayObject = child;
			super.addChildAt(result, 0);
			super.addChildAt(m_background, 1);
			super.addChildAt(m_viewContainer, 2);
			super.addChild(m_border);
			
			return result;
		}
		
	}//c
}